💡本筆記內容源自溫宏斌老師的物件導向程式設計OCW。
(一)概念
1.衍生類別繼承多個父類別,作法一個個列出來被繼承的類別(父類別)。
2.回顧繼承特性(假設class Boo 公開繼承Bum和Foo)
(1)支援Bum和Foo公開/受保護的方法
(2)由於是衍生類別,Boo的物件可以轉換成Bum或Foo的物件(如前一章圓vs圓柱體)。
(3)當你想創造新的Boo物件,你也會呼叫Bum或Foo預設的constructors,並用反著呼叫destructors。
💡注意: 呼叫建構子的順序是按照宣告順序從左到右,而非初始化的順序(見本章第三部分)。
3.多重繼承以圖示來看如下圖
]
4.缺點:
多重繼承較單繼承來得容易混淆,但如果用到多重繼承,且兩個父類別有相同的member,就要使用界定符號resolution operator(::)。
class Sofa{
public: void sit(){
cout << "sit" <<endl;}
void SetWeight(int a=0){weight=a;}
....
};
class Bed{
public: void lie(){
cout << "lie" <<endl;}
void SetWeight(int a=0){weight=a;}
....
};
//公開繼承
class Sofabed:public Sofa, public Bed{{
public: void fold(){
cout << "fold" <<endl;}
//
int main()
{
Sofabed myfur;
myfur.sit();
myfur.lie();
return 0;
}
(二)多重繼承產生的模糊地帶
1.發生情境
(1)從父類別呼叫了相同名字的member
(2)呼叫到共同基礎類別的member,也就是說,父類別也是繼承某類別來的(p.9)
2.重載同名成員以後出現的副作用:
(1)重載基礎類別底下同名的member後,會使他們不能直接被存取(access)。
//基礎類別
class CB{
public:
void f(){cout << "CB's f()"<<endl;}
void f(int x){cout << "CB's f()"<<endl;}
}
//衍生類別:公開繼承
class CD: public CB{
public:
void f(){cout << "CD's f()"<<endl;}
}
//main()
CD obj;
obj.f();
obj.f(5); //錯誤,會顯示不該有參數
例如上面的程式碼因衍生類別CD裡面有void f()蓋掉了基礎類別中void f()和void f(int x),即便參數數量不同,但因函式名稱相同還是被蓋掉,所以下半部obj.f(5)會error,除非用obj.CB::f(5)。
(2)父類別也是繼承其他類別而來,除了產生歧異性或混淆不清的情況,還會浪費記憶體。
如上圖,CB繼承CA、CC繼承CA,CD包含了兩個copy的CA。
用程式碼來看,可以發現錯誤在於:
//衍生類別:公開繼承
class CD: public CB, public CC{
public:
int w;
CD(int a = 0,int b = 0,int c = 0,int d = 0,int e = 0):CB(a,b),CC(c,d){w=e;}
....
}
//main()
CD obj(5,4,3,2,1);//錯誤,因為CB和CC中的x應該是同一個,但因硬寫成兩次,導致兩個x copy最終出現的值不同。
3.解方
虛擬基礎類別(如下)
(一)概念
虛基類的用途在於當定義一個衍生類別,且這個衍生類別是繼承到同一個基礎類別時,只保留一份的copy,如前例CB和CC都有繼承到CA的x,此時只會留下一份x。
//CA
class CA{
public:
int x;
CA(int a=0){x=a;}
};
//CB:虛擬繼承CA
class CB:**virtual** public CA{
public:
int y;
CB(int a=0, int b=0):CA(a){y=b;}
};
//CC:虛擬繼承CA
class CB:**virtual** public CA{
public:
int z;
CC(int a=0, int b=0):CA(a){z=b;}
};
//CD:
class CD: public CB, public CC{
public:
int w;
//在呼叫CB,CC以前,用CA的建構子初始化x(a)欄位,x的結果會是一致的。
CD(int a = 0,int b = 0,int c = 0,int d = 0,int e = 0):**CA(a)**,CB(a,b),CC(c,d){w=e;}
....
}
⭐⭐虛基類的constructor要寫在最前面,不能沒寫CA(a),否則他就會回到預設初始值
(二)重構前面沙發的例子
1.redesign SofaBed在Sofa和Bed之上再定一層
//作為共同的base class
class Furniture{
protected: int weight;
public:
void SetWeight(int a=0){weight=a;}
...
};
//Sofa
class Sofa: virtual public Furniture {
public: void sit(){
cout << "sit" <<endl;}
};
class Bed: virtual public Furniture {
public: void lie(){
cout << "lie" <<endl;}
};
//Sofabed
class Sofabed:public Sofa, public Bed{
public: void fold(){
cout << "fold" <<endl;}
};
//
int main()
{
Sofabed obj;
obj.SetWeight(100);//因為使用虛基類,所以是Furniture的SetWeight,把她餵進Furniture的void SetWeight。
return 0;
}
//Bed
1.從宣告的順序,而非始化順序找虛基類是誰,先做虛基類的初始化,做完以後才找其他類別的初始化,以下圖為例,由於宣告順序是CB再來CC,所以會從CB找到虛基類CA,初始化CA->CB->CC。
2.解構子是從相反順序
目前無
多型